using System;
using System.Text.RegularExpressions;
using Domain;
using System.Globalization;

namespace Presentation
{
    public class TrianglePresenter
    {
        public delegate void LogEventHandler(ILogger log);

        private readonly ITriangleView view;
        private Triangle triangle;

        private readonly string[] separators;
        private const string EMPTY_STRING = "";

        public TrianglePresenter(ITriangleView view)
        {
            this.view = view;
            this.view.CalculateParameters += CalculateParameters;
            this.view.WriteToLog += WriteToLog;

            separators = new [] {",", " "};
        }
        public void CalculateParameters(object sender, EventArgs e)
        {
            CleanOutput();

            CalculateSquare();
            CalculatePerimeter();
            CalculateLenghtsOfSides();
            CalculateAnglesInRadians();
        }
        public void WriteToLog (ILogger logger)
        {
            logger.Write("EXPERIMENT STARTED");
            logger.Write(EMPTY_STRING, false);
            logger.Write("Input data:");
            logger.Write("Point A has coordinates " + view.PointA, false);
            logger.Write("Point B has coordinates " + view.PointB, false);
            logger.Write("Point C has coordinates " + view.PointC, false);
            logger.Write(EMPTY_STRING, false);
            logger.Write("Output data:");
            if (IsParameterVisible(view.ErrorMessage))
            {
                logger.Write("ERROR: " + view.ErrorMessage, false);
            }
            else
            {
                logger.Write("Perimeter = " + view.Perimeter, false);
                logger.Write("Square = " + view.Square, false);
                logger.Write("Length AB = " + view.AB, false);
                logger.Write("Length BC = " + view.BC, false);
                logger.Write("Length CA = " + view.CA, false);
                logger.Write("Angle BAC = " + view.Alpha, false);
                logger.Write("Angle CBA = " + view.Beta, false);
                logger.Write("Angle ACB = " + view.Gamma, false);
            }
            logger.Write(EMPTY_STRING, false);
            logger.Write("EXPERIMENT FINISHED");
            logger.Write(EMPTY_STRING, false);
        }
        public bool IsParameterVisible(string parameter)
        {
            return !string.IsNullOrEmpty(parameter);
        }
        private bool CreateTriangle()
        {
            var wasCreated = true;
            try
            {
                var A = GetCoordinates(view.PointA);
                var B = GetCoordinates(view.PointB);
                var C = GetCoordinates(view.PointC);

                triangle = new Triangle(A, B, C);
            }
            catch (IncorrectInputException e)
            {
                wasCreated = false;
                view.ErrorMessage = e.Message;
            }
            return wasCreated;
        }
        public void CalculatePerimeter()
        {
            if (!CreateTriangle()) return;
            view.Perimeter = ToString(triangle.Perimeter);
        }
        public void CalculateSquare()
        {
            if (!CreateTriangle()) return;
            view.Square = ToString(triangle.Square);
        }
        public void CalculateLenghtsOfSides()
        {
            if (!CreateTriangle()) return;

            view.AB = ToString(triangle.AB);
            view.BC = ToString(triangle.BC);
            view.CA = ToString(triangle.CA);
        }
        public void CalculateAnglesInRadians()
        {
            if (!CreateTriangle()) return;
            try
            {
                view.Alpha = ToString(triangle.Alpha);
                view.Beta = ToString(triangle.Beta);
                view.Gamma = ToString(triangle.Gamma);
            }
            catch (BadAngleException)
            {
                view.ErrorMessage = ErrorMessage.IndeterminateAngle;
            }
        }
        private Point GetCoordinates(string input)
        {
            if ("" == input)
            {
                throw new IncorrectInputException(ErrorMessage.EmptyInput);
            }
            if (IsCorrectFormat(input))
            {
                var coordinates = input.Split(new[] {",", " "}, StringSplitOptions.RemoveEmptyEntries);
                var x = ToDouble(coordinates[0]);
                var y = ToDouble(coordinates[1]);
                var point = new Point(x, y);
                return point;
            }
            throw new IncorrectInputException(ErrorMessage.IncorrectInput + input);
        }
        private bool IsCorrectFormat(string input)
        {
            string[] coordinates = {@"-?[1-9]\d*\.?\d*", @"0\.\d*", "0"};
            foreach (var x in coordinates)
            {
                foreach (var y in coordinates)
                {
                    foreach (var separator in separators)
                    {
                        var pattern = @"\s*" + x + @"\s*" + separator + @"\s*" + y + @"\s*";
                        var regex = new Regex(pattern);
                        var match = regex.Match(input);
                        if (input.Equals(match.ToString()))
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
        private static double ToDouble(string input)
        {
            if (string.IsNullOrEmpty(input)) return 0d;
            return double.Parse(input, CultureInfo.InvariantCulture);
        }
        private static string ToString(double input)
        {
            return string.Format("{0:0.000}", input);
        }
        private void CleanOutput()
        {
            view.Perimeter = "";
            view.Square = "";
            view.AB = "";
            view.BC = "";
            view.CA = "";
            view.Alpha = "";
            view.Beta = "";
            view.Gamma = "";
            view.ErrorMessage = "";
        }
    }
}